#pragma once

//these are not thread safe, they are very simple implementations
//of the IParserProperty, IParserPropertyBag & IEnumParserProperties
//for use by the console test harness so parsers can be tested
//without activating them in SharePoint.

//Notice that even though we are a UNICODE project, this
//interface is explicitly cast as MULTI BYTE , that the 
//way that MSDN documents it so we go with that, 

static const int PARSER_TYPE_INT = 1;
static const int PARSER_TYPE_DOUBLE = 2;
static const int PARSER_TYPE_FILETIME = 3;
static const int PARSER_TYPE_BOOLEAN = 4;
static const int PARSER_TYPE_SHORTTEXT = 5;
static const int PARSER_TYPE_LONGTEXT = 6;


class CFakeProperty:public IParserProperty
{
private:
	LONG m_ReferenceCount;
	CStringA m_Name;
	int	m_Int;
	int m_Type ;
	double m_Double;
	FILETIME m_FileTime;
	VARIANT_BOOL m_Bool;
	CStringA m_Text;

	//Yes this is ugly so do your own :)
	//no seriously we only parse the format
	//we see in Office files which is the W3CDTF 
	//time format 2008-06-02T10:10:32Z
	//time zones are adjusted out and the
	//return value is GMT
	
	FILETIME W3CDTFDateStringToFILETIME(CStringA Date)
	{
		FILETIME returnValue = {0};
		SYSTEMTIME stDate = {0};
		stDate.wDay=1;
		stDate.wMonth=1;

		CStringA datePart;
		CStringA timePart;
		CStringA zonePart;

		CStringA tempString;
		int posT = Date.Find("T",0);
		if(posT == -1)
		{
			datePart = Date;
		}
		else
		{
			datePart = Date.Mid(0,posT);
			timePart = Date.Mid(posT +1);
		}
		int posZ = timePart.Find("Z",0);
		if (-1== posZ) posZ = timePart.Find("z",0);
		if (-1== posZ) posZ = timePart.Find("+",0);
		if (-1== posZ) posZ = timePart.Find("-",0);
		if(-1 != posZ)
		{
			zonePart = timePart.Mid(posZ);
			timePart = timePart.Mid(0,posZ);
		}


		if(datePart.GetLength() <4) goto CONVERTTIME;
		int posEnd = datePart.Find("-",0);
		if(-1 == posEnd) posEnd = datePart.GetLength();
		tempString = datePart.Mid(0,posEnd);
		int posStart = posEnd+1;
		stDate.wYear = atoi(tempString);

		if(posStart>= datePart.GetLength()) goto CONVERTTIME;
		posEnd = datePart.Find("-",posStart);
		if(-1 == posEnd) posEnd = datePart.GetLength();
		tempString = datePart.Mid(posStart,posEnd - posStart);
		posStart = posEnd+1;
		stDate.wMonth = atoi(tempString);

		if(posStart>= datePart.GetLength()) goto CONVERTTIME;
		posEnd = datePart.GetLength();
		tempString =  datePart.Mid(posStart,posEnd - posStart);
		stDate.wDay = atoi(tempString);

		//Done with the date now start the time parsing

		if(timePart.GetLength() <=0) goto CONVERTTIME;
		posEnd = timePart.Find(":",0);
		if(-1 == posEnd) posEnd = timePart.GetLength();
		tempString = timePart.Mid(0,posEnd);
		posStart = posEnd+1;
		stDate.wHour = atoi(tempString);

		if(posStart>= timePart.GetLength()) goto CONVERTTIME;
		posEnd = timePart.Find(":",posStart);
		if(-1 == posEnd) posEnd = timePart.GetLength();
		tempString = timePart.Mid(posStart,posEnd - posStart);
		posStart = posEnd+1;
		stDate.wMinute = atoi(tempString);

		
		if(posStart>= timePart.GetLength()) goto CONVERTTIME;
		posEnd = timePart.Find(":",posStart);
		if(-1 == posEnd) posEnd = timePart.Find(".",posStart);
		if(-1 == posEnd) posEnd = timePart.GetLength();
		tempString = timePart.Mid(posStart,posEnd - posStart);
		posStart = posEnd+1;
		stDate.wSecond = atoi(tempString);

		if(posStart>= timePart.GetLength()) goto CONVERTTIME;
		posEnd = timePart.GetLength();
		tempString = timePart.Mid(posStart,posEnd- posStart);
		stDate.wMilliseconds = atoi(tempString);
		
		//And now Time Zone , we want to revert everything
		//to Zulu time (GMT) so if there is a time zone
		//then process it and adjust the base time
		if(zonePart.IsEmpty()) goto CONVERTTIME;
		if("Z" == zonePart) goto CONVERTTIME;
		if("z" == zonePart) goto CONVERTTIME;
		bool addTime = true;
		int hoursOffset = 0;
		int minutesOffset = 0;
		tempString = timePart.Mid(0,1);
		if("-" == tempString) addTime = false;
		posStart = 2;
		posEnd = zonePart.Find(":",posStart);
		if(-1 == posEnd) posEnd = zonePart.GetLength();
		tempString = zonePart.Mid(posStart,posEnd - posStart);
		posStart = posEnd+1;
		hoursOffset = atoi(tempString);
		if(posStart < zonePart.GetLength())
		{
			posEnd = zonePart.GetLength();
			tempString = zonePart.Mid(posStart,posEnd - posStart);
			minutesOffset = atoi(tempString);
		}
		if(addTime)
		{
			//yes this is correct we are adding to the offset not to the 
			//clock since timezones are offsets from zulu time
			stDate.wHour -= hoursOffset;
			stDate.wMinute -= minutesOffset;
		}
		else
		{
			stDate.wHour += hoursOffset;
			stDate.wMinute += minutesOffset;
		}


CONVERTTIME:
		
		::SystemTimeToFileTime(&stDate,&returnValue);
		return returnValue;
	}




public:

	CFakeProperty(CStringA propertyName)
	{
		m_ReferenceCount = 1;
		this->SetEmpty();
		m_Name = propertyName;
	}

	~CFakeProperty(void)
	{
	}

	virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
	{
		if(NULL == ppvObj) return E_INVALIDARG;
		*ppvObj = NULL;

		if((riid == IID_IUnknown) || (riid == IID_IParserProperty))
		{
			*ppvObj = this;
			((IUnknown*)*ppvObj)->AddRef();
			return S_OK;
		}
		return E_NOINTERFACE;
	}

	virtual ULONG STDMETHODCALLTYPE  AddRef()
	{
		::InterlockedIncrement(&m_ReferenceCount);
		return m_ReferenceCount;
	}

	virtual ULONG STDMETHODCALLTYPE  Release()
	{
		::InterlockedDecrement(&m_ReferenceCount);
		if(m_ReferenceCount <=0) delete this;
		return m_ReferenceCount;
	}

	virtual HRESULT __stdcall GetName(CHAR* *pval)
	{
		*pval = (char*)::malloc(m_Name.GetAllocLength());
		strcpy_s(*pval,m_Name.GetLength(),m_Name.GetBuffer());
		return S_OK;
	}

	virtual HRESULT __stdcall  GetValueType(int *pitype)
	{
		*pitype=m_Type;
		return S_OK;
	}

	virtual HRESULT __stdcall  GetNumber(int *pval)
	{
		*pval = m_Int;
		return S_OK;
	}

	virtual HRESULT __stdcall  SetNumber(int val)
	{
		m_Int = val;
		m_Type = PARSER_TYPE_INT;
		return S_OK;
	}

	virtual HRESULT __stdcall  GetDouble(double *pval)
	{
		*pval = m_Double;
		return S_OK;
	}

	virtual HRESULT __stdcall  SetDouble(double val)
	{
		m_Double = val;
		m_Type = PARSER_TYPE_DOUBLE;
		return S_OK;
	}

	virtual HRESULT __stdcall  GetDateTime(FILETIME *pval)
	{
		pval->dwHighDateTime = m_FileTime.dwHighDateTime;
		pval->dwLowDateTime = m_FileTime.dwLowDateTime;
		return S_OK;
	}

	virtual HRESULT __stdcall  SetDateTime(FILETIME val)
	{
		m_FileTime.dwHighDateTime = val.dwHighDateTime;
		m_FileTime.dwLowDateTime = val.dwLowDateTime;
		m_Type = PARSER_TYPE_FILETIME;
		return S_OK;
	}

	virtual HRESULT __stdcall  GetBoolean(VARIANT_BOOL *pval)
	{
		*pval = m_Bool;
		return S_OK;
	}

	virtual HRESULT __stdcall  SetBoolean(VARIANT_BOOL val)
	{
		m_Bool = val;
		m_Type = PARSER_TYPE_BOOLEAN;
		return S_OK;
	}

	virtual HRESULT __stdcall  GetShortText(CHAR* *pval)
	{
		CStringA shortText = m_Text;
		if(shortText.GetLength() > 255)
		{
			shortText = shortText.Mid(0,255);
		}
		*pval = (char*)::malloc(shortText.GetAllocLength());
		strcpy_s(*pval,shortText.GetLength(),shortText.GetBuffer());
		return S_OK;
	}

	virtual HRESULT __stdcall  SetShortText(CHAR* val)
	{
		m_Text = val;
		m_Type = PARSER_TYPE_SHORTTEXT;
		return S_OK;
	}

	virtual HRESULT __stdcall  GetLongText(CHAR* *pval)
	{
		*pval = (char*)::malloc(m_Text.GetAllocLength());
		strcpy_s(*pval,m_Text.GetLength(),m_Text.GetBuffer());
		return S_OK;
	}

	virtual HRESULT __stdcall  SetLongText(CHAR* val)
	{
		m_Text = val;
		m_Type = PARSER_TYPE_LONGTEXT;
		return S_OK;
	}

	virtual HRESULT __stdcall  SetValueFromString(CHAR* val,int pvtHint)
	{
		this->SetEmpty();
		HRESULT returnValue = S_OK;
		CStringA tempValue(val);
		switch(pvtHint)
		{
		case PARSER_TYPE_LONGTEXT:
			{
				this->SetLongText(val);
			}
			break;
		case PARSER_TYPE_SHORTTEXT:
			{
				this->SetShortText(val);
			}
			break;
		case PARSER_TYPE_BOOLEAN:
			{
				m_Type = PARSER_TYPE_BOOLEAN;
				m_Bool = VARIANT_FALSE;
				tempValue.MakeLower().Trim();
				if("true" == tempValue) m_Bool = VARIANT_TRUE;
				if("yes" == tempValue) m_Bool = VARIANT_TRUE;
				if("-1" == tempValue) m_Bool = VARIANT_TRUE;
				if("okdokey" == tempValue) m_Bool = VARIANT_TRUE;
			}
			break;
		case PARSER_TYPE_FILETIME:
			{
				m_Type = PARSER_TYPE_FILETIME;
				m_FileTime = W3CDTFDateStringToFILETIME(tempValue);
			}
			break;
		case PARSER_TYPE_DOUBLE:
			{
				m_Type = PARSER_TYPE_DOUBLE;
				m_Double = atof(val);

			}
			break;
		case PARSER_TYPE_INT:
			{
				m_Type = PARSER_TYPE_INT;
				m_Int = atoi(val);
			}
			break;
		default:
			returnValue = E_INVALIDARG;
			break;
		}

		return returnValue;
	}

	virtual HRESULT __stdcall  SetEmpty()
	{
		m_Type = 0;
		m_Int =0;
		m_Double =0;
		m_FileTime.dwHighDateTime=0;
		m_FileTime.dwLowDateTime=0;
		m_Bool = VARIANT_FALSE;
		m_Text.Empty();
		return S_OK;
	}

};


class CFakePropertyBag:public IParserPropertyBag, IEnumParserProperties
{
private:
	LONG m_ReferenceCount;
	CAtlMap <CStringA, CFakeProperty* > m_PropertyBag;
	POSITION m_CurrentEnumPosition;

public:

	CFakePropertyBag(void)
	{
		m_ReferenceCount = 1;
		m_CurrentEnumPosition = m_PropertyBag.GetStartPosition();
	}

	~CFakePropertyBag(void)
	{
		for(POSITION pNextPos = m_PropertyBag.GetStartPosition(); pNextPos != NULL; ) 
		{
			CFakeProperty* lpFakeProp = m_PropertyBag.GetNextValue(pNextPos);
			lpFakeProp->Release();
		}
	}

	virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
	{
		if(NULL == ppvObj) return E_INVALIDARG;
		*ppvObj = NULL;

		if((riid == IID_IUnknown) || (riid == IID_IParserPropertyBag)|| (riid == IID_IEnumParserProperties))
		{
			*ppvObj = this;
			((IUnknown*)*ppvObj)->AddRef();
			return S_OK;
		}
		return E_NOINTERFACE;
	}

	virtual ULONG STDMETHODCALLTYPE  AddRef()
	{
		::InterlockedIncrement(&m_ReferenceCount);
		return m_ReferenceCount;
	}

	virtual ULONG STDMETHODCALLTYPE  Release()
	{
		::InterlockedDecrement(&m_ReferenceCount);
		if(m_ReferenceCount <=0) delete this;
		return m_ReferenceCount;
	}

	virtual HRESULT __stdcall Enum (IEnumParserProperties * * ppenum )
	{
		*ppenum = this;
		return S_OK;
	}

	virtual HRESULT __stdcall DeleteProp (char * szName )
	{
		CFakeProperty* lpFakeProp = NULL;
		CStringA keyValue(szName);
		if(m_PropertyBag.Lookup(keyValue,lpFakeProp))
		{
			m_PropertyBag.RemoveKey(keyValue);
			lpFakeProp->Release();
			return S_OK;
		}
		return S_FALSE;
	}

	virtual HRESULT __stdcall NewProp (char * szName,IParserProperty * * ppiprop ) 
	{
		HRESULT hRes = this->DeleteProp(szName);
		if(SUCCEEDED(hRes))
		{
			CStringA keyValue(szName);
			CFakeProperty* lpFakeProp = new CFakeProperty(keyValue);
			m_PropertyBag[keyValue] = lpFakeProp;
			lpFakeProp->AddRef();
			*ppiprop = (IParserProperty*) lpFakeProp;
		}
		return S_OK;
	}

	virtual HRESULT __stdcall FindProp (char * szName, IParserProperty * * ppiprop )
	{
		CFakeProperty* lpFakeProp = NULL;
		CStringA keyValue(szName);
		if(m_PropertyBag.Lookup(keyValue,lpFakeProp))
		{
			*ppiprop = (IParserProperty*) lpFakeProp;
			return S_OK;
		}
		else
		{
			return E_FAIL;
		}
	}

	virtual HRESULT __stdcall ProcessLink ( char * val, char * * pval ) 
	{
		return E_NOTIMPL;
	}

	virtual HRESULT __stdcall GetSchemaXml (   char * * pszXml ) 
	{
		return E_NOTIMPL;
	}

	virtual HRESULT __stdcall EncodeName ( char * sz, char * szOut, int * pcchOut ) 
	{
		return E_NOTIMPL;
	}

	virtual HRESULT __stdcall DecodeName ( char * sz, char * szOut, int * pcchOut ) 
	{
		return E_NOTIMPL;
	}
	virtual HRESULT __stdcall GetContentTypeSchemaXml ( char * * pszXml )
	{
		return E_NOTIMPL;
	}

	//Enum interfaces for the collection not thread safe !
	virtual HRESULT __stdcall Reset ( ) 
	{
		m_CurrentEnumPosition = m_PropertyBag.GetStartPosition();
		return S_OK;
	}
	virtual HRESULT __stdcall Next (struct IParserProperty * * ppiprop ) 
	{
		if(NULL != m_CurrentEnumPosition)
		{
			CFakeProperty* lpFakeProp  = m_PropertyBag.GetNextValue(m_CurrentEnumPosition);
			*ppiprop = (IParserProperty*) lpFakeProp;
			return S_OK;
		}
		else
		{
			return E_FAIL;
		}
	}

};
